﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Text;
using System.Xml.Linq;
using System.Xml.Serialization;
using Fwql.Net.Utils;

namespace Fwql.Net.Extensions
{
    public static class ObjectExtension
    {
        #region Convert

        /// <summary>
        /// 判断对象是否与指定集合中的任一对象相等
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "obj"></param>
        /// <param name = "values"></param>
        /// <returns></returns>
        public static bool EqualsAny<T>(this T obj, params T[] values)
        {
            return (Array.IndexOf(values, obj) != -1);
        }

        /// <summary>
        /// 判断对象是否与指定集合中的所有对象都不相等
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "obj"></param>
        /// <param name = "values"></param>
        /// <returns></returns>
        public static bool EqualsNone<T>(this T obj, params T[] values)
        {
            return (obj.EqualsAny(values) == false);
        }

        /// <summary>
        /// 将对象转换为指定类型，如果转换失败则返回指定类型的默认值
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "value"></param>
        /// <returns></returns>
        public static T ConvertTo<T>(this object value)
        {
            return value.ConvertTo(default(T));
        }

        /// <summary>
        /// 将对象转换为指定类型，如果转换失败则返回默认值
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "value"></param>
        /// <param name = "defaultValue"></param>
        /// <returns></returns>
        public static T ConvertTo<T>(this object value, T defaultValue)
        {
            if (value != null)
            {
                var targetType = typeof (T);

                if (value.GetType() == targetType)
                    return (T) value;

                var converter = TypeDescriptor.GetConverter(value);
                if (converter != null)
                {
                    if (converter.CanConvertTo(targetType))
                        return (T) converter.ConvertTo(value, targetType);
                }

                converter = TypeDescriptor.GetConverter(targetType);
                if (converter != null)
                {
                    if (converter.CanConvertFrom(value.GetType()))
                        return (T) converter.ConvertFrom(value);
                }
            }
            return defaultValue;
        }

        /// <summary>
        /// 将对象转换为指定类型，如果转换失败则返回默认值
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "value"></param>
        /// <param name = "defaultValue"></param>
        /// <param name = "ignoreException">如果为<c>true</c>则在方法内部捕捉异常</param>
        /// <returns></returns>
        public static T ConvertTo<T>(this object value, T defaultValue, bool ignoreException)
        {
            if (ignoreException)
            {
                try
                {
                    return value.ConvertTo<T>();
                }
                catch
                {
                    return defaultValue;
                }
            }
            return value.ConvertTo<T>();
        }

        /// <summary>
        /// 判断是否可以将对象安全转换成指定类型
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "value"></param>
        /// <returns></returns>
        public static bool CanConvertTo<T>(this object value)
        {
            if (value != null)
            {
                var targetType = typeof (T);

                var converter = TypeDescriptor.GetConverter(value);
                if (converter != null)
                {
                    if (converter.CanConvertTo(targetType))
                        return true;
                }

                converter = TypeDescriptor.GetConverter(targetType);
                if (converter != null)
                {
                    if (converter.CanConvertFrom(value.GetType()))
                        return true;
                }
            }
            return false;
        }

        #endregion

        #region Common

        /// <summary>
        /// 转为数据库可接受的值，如果为指定类型的默认值，则返回DBNull.Value
        /// </summary>
        /// <typeparam name = "T"></typeparam>
        /// <param name = "value">The value.</param>
        /// <returns></returns>
        public static object ToDatabaseValue<T>(this T value)
        {
            return (value.Equals(default(T)) ? DBNull.Value : (object) value);
        }

        /// <summary>
        /// 判断对象是否为null
        /// </summary>
        /// <param name = "target"></param>
        public static bool IsNull(this object target)
        {
            var ret = IsNull<object>(target);
            return ret;
        }

        /// <summary>
        /// 判断对象是否为null
        /// </summary>
        /// <typeparam name = "T">类型</typeparam>
        /// <param name = "target"></param>
        public static bool IsNull<T>(this T target)
        {
            var result = ReferenceEquals(target, null);
            return result;
        }

        /// <summary>
        /// 判断对象是否不为null
        /// </summary>
        /// <param name = "target"></param>
        public static bool IsNotNull(this object target)
        {
            var ret = IsNotNull<object>(target);
            return ret;
        }

        /// <summary>
        /// 判断对象是否不为null
        /// </summary>
        /// <typeparam name = "T">类型</typeparam>
        /// <param name = "target"></param>
        public static bool IsNotNull<T>(this T target)
        {
            var result = !ReferenceEquals(target, null);
            return result;
        }

        /// <summary>
        /// 如果为null，返回notNullValue，否则返回target
        /// </summary>
        /// <typeparam name = "T">类型</typeparam>
        /// <param name = "target"></param>
        /// <param name = "notNullValue"></param>
        public static T NotNull<T>(this T target, T notNullValue)
        {
            return ReferenceEquals(target, null) ? notNullValue : target;
        }

        /// <summary>
        /// 如果为null，返回notNullValueProvider()的值，否则返回target
        /// </summary>
        /// <typeparam name = "T">类型</typeparam>
        /// <param name = "target"></param>
        /// <param name = "notNullValueProvider"></param>
        public static T NotNull<T>(this T target, Func<T> notNullValueProvider)
        {
            return ReferenceEquals(target, null) ? notNullValueProvider() : target;
        }

        /*
        /// <summary>
        /// 	get a string representation of a given object.
        /// </summary>
        /// <param name = "o">the object to dump</param>
        /// <param name = "flags">BindingFlags to use for reflection</param>
        /// <param name = "maxArrayElements">Number of elements to show for IEnumerables</param>
        /// <returns></returns>
        public static string ToStringDump(this object o,
                                          BindingFlags flags =
                                              BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                                          int maxArrayElements = 5)
        {
            return ToStringDumpInternal(o.ToXElement(flags,
                                                     maxArrayElements)).Aggregate(String.Empty,
                                                                                  (str,
                                                                      
                                                                                      el) => str + el);
        }

        private static IEnumerable<string> ToStringDumpInternal(XContainer toXElement)
        {
            foreach (var xElement in toXElement.Elements().OrderBy(o => o.Name.ToString()))
            {
                if (xElement.HasElements)
                {
                    foreach (var el in ToStringDumpInternal(xElement))
                        yield return "{" + String.Format("{0}={1}",
                                                         xElement.Name,
                                                         el) + "}";
                }
                else
                    yield return "{" + String.Format("{0}={1}",
                                                     xElement.Name,
                                                     xElement.Value) + "}";
            }
        }

        /// <summary>
        /// 	get a html-table representation of a given object.
        /// </summary>
        /// <param name = "o">the object to dump</param>
        /// <param name = "flags">BindingFlags to use for reflection</param>
        /// <param name = "maxArrayElements">Number of elements to show for IEnumerables</param>
        /// <returns></returns>
        public static string ToHtmlTable(this object o,
                                         BindingFlags flags =
                                             BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                                         int maxArrayElements = 5)
        {
            return ToHtmlTableInternal(o.ToXElement(flags,
                                                    maxArrayElements),
                                       0).Aggregate(String.Empty,
                                                    (str,
                                                     el) => str + el);
        }

        private static IEnumerable<string> ToHtmlTableInternal(XContainer xel,
                                                               int padding)
        {
            yield return FormatHtmlLine("<table>",
                                        padding);
            yield return FormatHtmlLine("<tr><th>Attribute</th><th>Value</th></tr>",
                                        padding + 1);
            foreach (var xElement in xel.Elements().OrderBy(o => o.Name.ToString()))
            {
                if (xElement.HasElements)
                {
                    yield return FormatHtmlLine(String.Format("<tr><td>{0}</td><td>",
                                                              xElement.Name),
                                                padding + 1);
                    foreach (var el in ToHtmlTableInternal(xElement,
                                                           padding + 2))
                        yield return el;
                    yield return FormatHtmlLine("</td></tr>",
                                                padding + 1);
                }
                else
                    yield return FormatHtmlLine(String.Format("<tr><td>{0}</td><td>{1}</td></tr>",
                                                              xElement.Name,
                                                              HttpUtility.HtmlEncode(xElement.Value)),
                                                padding + 1);
            }
            yield return FormatHtmlLine("</table>",
                                        padding);
        }

        private static string FormatHtmlLine(string tag,
                                             int padding)
        {
            return String.Format("{0}{1}{2}",
                                 String.Empty.PadRight(padding,
                                                       '\t'),
                                 tag,
                                 Environment.NewLine);
        }
        */

        /// <summary>
        /// 将对象转成XElement
        /// </summary>
        /// <param name = "o"></param>
        /// <param name = "flags">BindingFlags，反射</param>
        /// <returns></returns>
        public static XElement ToXElement(this object o,
                                          BindingFlags flags = BindingFlags.Instance | BindingFlags.Public)
        {
            try
            {
                return ToXElementInternal(o, new HashSet<object>(), flags);
            }
            catch
            {
                return new XElement(o.GetType().Name);
            }
        }

        private static XElement ToXElementInternal(object o, ICollection<object> visited, BindingFlags flags)
        {
            if (o == null)
                return new XElement("null");
            if (visited.Contains(o))
                return new XElement("cyclic reference");

            var type = o.GetType();
            if (!type.IsValueType)
                visited.Add(o);

            var elems = new XElement(CleanName(type.Name, type.IsArray));

            if (!NeedRecursion(type, o))
            {
                elems.Add(new XElement(CleanName(type.Name, type.IsArray), String.Empty + o));
                return elems;
            }
            if (o is IEnumerable)
            {
                foreach (var el in o as IEnumerable)
                {
                    var subtype = el.GetType();
                    elems.Add(NeedRecursion(subtype, el)
                                  ? ToXElementInternal(el, visited, flags)
                                  : new XElement(CleanName(subtype.Name, subtype.IsArray), el));
                }
                return elems;
            }
            foreach (var propertyInfo in from propertyInfo in type.GetProperties(flags)
                                         where propertyInfo.CanRead
                                         select propertyInfo)
            {
                var value = GetValue(o, propertyInfo);
                elems.Add(NeedRecursion(propertyInfo.PropertyType, value)
                              ? new XElement(CleanName(propertyInfo.Name, propertyInfo.PropertyType.IsArray),
                                             ToXElementInternal(value, visited, flags))
                              : new XElement(CleanName(propertyInfo.Name, propertyInfo.PropertyType.IsArray),
                                             String.Empty + value));
            }
            foreach (var fieldInfo in type.GetFields())
            {
                var value = fieldInfo.GetValue(o);
                elems.Add(NeedRecursion(fieldInfo.FieldType, value)
                              ? new XElement(CleanName(fieldInfo.Name, fieldInfo.FieldType.IsArray),
                                             ToXElementInternal(value, visited, flags))
                              : new XElement(CleanName(fieldInfo.Name, fieldInfo.FieldType.IsArray),
                                             String.Empty + value));
            }
            return elems;
        }

        private static bool NeedRecursion(Type type, object o)
        {
            return o != null &&
                   (!type.IsPrimitive &&
                    !(o is String || o is DateTime || o is DateTimeOffset || o is TimeSpan || o is Delegate || o is Enum ||
                      o is Decimal || o is Guid));
        }

        private static object GetValue(object o, PropertyInfo propertyInfo)
        {
            object value;
            try
            {
                value = propertyInfo.GetValue(o, null);
            }
            catch
            {
                try
                {
                    value = propertyInfo.GetValue(o,
                                                  new object[]
                                                  {
                                                      0
                                                  });
                }
                catch
                {
                    value = null;
                }
            }
            return value;
        }

        private static string CleanName(IEnumerable<char> name, bool isArray)
        {
            var sb = new StringBuilder();
            foreach (var c in name.Where(c => Char.IsLetterOrDigit(c) && c != '`').Select(c => c))
                sb.Append(c);
            if (isArray)
                sb.Append("Array");
            return sb.ToString();
        }

        /// <summary>
        /// 对象类型转换
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="targetType">目标类型</param>
        /// <returns></returns>
        public static object DynamicCast(this object obj, Type targetType)
        {
            // First, it might be just a simple situation
            if (targetType.IsAssignableFrom(obj.GetType()))
                return obj;

            // If not, we need to find a cast operator. The operator
            // may be explicit or implicit and may be included in
            // either of the two types...
            const BindingFlags pubStatBinding = BindingFlags.Public | BindingFlags.Static;
            var originType = obj.GetType();
            String[] names = {
                                 "op_Implicit", "op_Explicit"
                             };

            var castMethod =
                targetType.GetMethods(pubStatBinding).Union(originType.GetMethods(pubStatBinding)).FirstOrDefault(
                    itm =>
                    itm.ReturnType.Equals(targetType) && itm.GetParameters().Length == 1 &&
                    itm.GetParameters()[0].ParameterType.IsAssignableFrom(originType) && names.Contains(itm.Name));
            if (null != castMethod)
                return castMethod.Invoke(null,
                                         new[]
                                         {
                                             obj
                                         });
            throw new InvalidOperationException(String.Format("没有合适的类型转换器将 {0} 转换为 {1}.",
                                                              originType.Name,
                                                              targetType.Name));
        }

        /// <summary>
        /// 递归直到对象为null，并返回递归计数
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="item"></param>
        /// <param name="function">谓语</param>
        /// <returns>返回计数</returns>
        public static int CountLoopsToNull<T>(this T item, Func<T, T> function) where T : class
        {
            var num = 0;
            while ((item = function(item)) != null)
            {
                num++;
            }
            return num;
        }

        /// <summary>
        /// 递归找出指定类型的实例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TTarget">指定类型</typeparam>
        /// <param name="item"></param>
        /// <param name="function"></param>
        /// <returns></returns>
        /// <example><code>
        /// var tree = ...
        /// var node = tree.FindNodeByValue("");
        /// var parentByType = node.FindTypeByRecursion%lt;TheType&gt;(n => n.Parent);
        /// </code></example>
        public static TTarget FindTypeByRecursion<T, TTarget>(this T item, Func<T, T> function) where T : class
            where TTarget : class, T
        {
            do
            {
                if (item is TTarget)
                    return (TTarget) item;
            } while ((item = function(item)) != null);
            return null;
        }

        /// <summary>
        /// 对象深度拷贝
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static T Clone<T>(this T source)
        {
            if (!typeof (T).IsSerializable)
            {
                throw new ArgumentException("类型必须可序列化。", "source");
            }

            if (ReferenceEquals(source, null))
            {
                return default(T);
            }

            IFormatter formatter = new BinaryFormatter();
            Stream stream = new MemoryStream();
            using (stream)
            {
                formatter.Serialize(stream, source);
                stream.Seek(0, SeekOrigin.Begin);
                return (T) formatter.Deserialize(stream);
            }
        }

        #endregion

        #region Serialization

        #region Binary

        /// <summary>
        /// Binary序列化
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static byte[] SerializeToBinary(object obj)
        {
            Guard.NotDefault(obj, "obj");
            using (MemoryStream stream = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, obj);
                return stream.ToArray();
            }
        }

        /// <summary>
        /// Binary序列化并保存到文件
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="fileName">文件名(全路径)</param>
        public static void SerializeToBinaryFile(object obj, string fileName)
        {
            SerializeToBinary(obj).SaveToFile(fileName);
        }

        #endregion

        /// <summary>
        /// SOAP序列化
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="encoding">编码格式，默认UTF-8</param>
        /// <returns></returns>
        public static string SerializeToSOAP(object obj, Encoding encoding = null)
        {
            Guard.NotDefault(obj, "obj");
            using (MemoryStream stream = new MemoryStream())
            {
                SoapFormatter serializer = new SoapFormatter();
                serializer.Serialize(stream, obj);
                stream.Flush();
                if (encoding == null)
                {
                    encoding = Encoding.UTF8;
                }
                return encoding.GetString(stream.GetBuffer(), 0, (int) stream.Position);
            }
        }

        /// <summary>
        /// XML序列化
        /// </summary>
        /// <typeparam name="T">指定类型</typeparam>
        /// <param name="obj"></param>
        /// <param name="encoding">编码格式，默认UTF-8</param>
        /// <returns></returns>
        public static string SerializeToXML<T>(T obj, Encoding encoding = null) where T : class
        {
            Guard.NotDefault(obj, "obj");
            using (MemoryStream stream = new MemoryStream())
            {
                XmlSerializer serializer = new XmlSerializer(typeof (T));
                serializer.Serialize(stream, obj);
                stream.Flush();

                if (encoding == null)
                {
                    encoding = Encoding.UTF8;
                }
                return encoding.GetString(stream.GetBuffer(), 0, (int) stream.Position);
            }
        }

        #endregion

        #region Median

        /// <summary>
        /// 取得列表中大小相对居中的一个值
        /// </summary>
        public static T Median<T>(List<T> values)
        {
            if (values == null)
                return default(T);
            if (values.Count == 0)
                return default(T);
            values.Sort();
            return values[(values.Count/2)];
        }

        #endregion

        #region Reflection

        #region Type

        /// <summary>
        /// 判断对象类型是否为指定类型
        /// </summary>
        /// <typeparam name = "T">类型</typeparam>
        /// <param name = "obj"></param>
        /// <returns></returns>
        public static bool IsOfType<T>(this object obj)
        {
            return obj.IsOfType(typeof (T));
        }

        /// <summary>
        /// 判断对象类型是否为指定类型
        /// </summary>
        /// <param name = "obj"></param>
        /// <param name = "type">类型</param>
        /// <returns></returns>
        public static bool IsOfType(this object obj, Type type)
        {
            return (obj.GetType().Equals(type));
        }

        /// <summary>
        /// 判断对象类型是否为指定类型或继承自指定类型
        /// </summary>
        /// <typeparam name = "T">类型</typeparam>
        /// <param name = "obj"></param>
        /// <returns></returns>
        public static bool IsOfTypeOrInherits<T>(this object obj)
        {
            return obj.IsOfTypeOrInherits(typeof (T));
        }

        /// <summary>
        /// 判断对象类型是否为指定类型或继承自指定类型
        /// </summary>
        /// <param name = "obj">类型</param>
        /// <param name = "type"></param>
        /// <returns></returns>
        public static bool IsOfTypeOrInherits(this object obj, Type type)
        {
            var objectType = obj.GetType();

            do
            {
                if (objectType.Equals(type))
                    return true;
                if ((objectType == objectType.BaseType) || (objectType.BaseType == null))
                    return false;
                objectType = objectType.BaseType;
            } while (true);
        }

        /// <summary>
        /// 判断对象是否可转换为指定类型
        /// </summary>
        /// <typeparam name = "T">类型</typeparam>
        /// <param name = "obj"></param>
        /// <returns></returns>
        public static bool IsAssignableTo<T>(this object obj)
        {
            return obj.IsAssignableTo(typeof (T));
        }

        /// <summary>
        /// 判断对象是否可转换为指定类型
        /// </summary>
        /// <param name = "obj"></param>
        /// <param name = "type">类型</param>
        /// <returns></returns>
        public static bool IsAssignableTo(this object obj, Type type)
        {
            var objectType = obj.GetType();
            return type.IsAssignableFrom(objectType);
        }

        #endregion

        /// <summary>
        /// 通过反射动态调用对象方法
        /// </summary>
        /// <param name = "obj"></param>
        /// <param name = "methodName">方法名</param>
        /// <param name = "parameters">参数集合</param>
        /// <returns></returns>
        public static object InvokeMethod(this object obj, string methodName, params object[] parameters)
        {
            var method = obj.GetType().GetMethod(methodName);

            if (method == null)
                throw new ArgumentException(string.Format("类型 '{1}' 未声明名为 '{0}' 的方法。", methodName, obj), methodName);

            var value = method.Invoke(obj, parameters);
            return value;
        }

        /// <summary>
        /// 通过反射动态调用对象方法
        /// </summary>
        /// <typeparam name = "T">方法返回类型</typeparam>
        /// <param name = "obj"></param>
        /// <param name = "methodName">方法名</param>
        /// <param name = "parameters">参数集合</param>
        /// <returns></returns>
        public static T InvokeMethod<T>(this object obj, string methodName, params object[] parameters)
        {
            var method = obj.GetType().GetMethod(methodName);

            if (method == null)
                throw new ArgumentException(string.Format("类型 '{1}' 未声明名为 '{0}' 的方法。", methodName, obj), methodName);

            var value = method.Invoke(obj, parameters);
            return ((value is T) ? (T) value : default(T));
        }

        /// <summary>
        /// 通过反射动态取得对象的属性值
        /// </summary>
        /// <param name = "obj"></param>
        /// <param name = "propertyName">属性名</param>
        /// <returns></returns>
        public static object GetPropertyValue(this object obj, string propertyName)
        {
            return GetPropertyValue<object>(obj, propertyName, null);
        }

        /// <summary>
        /// 通过反射动态取得对象的属性值
        /// </summary>
        /// <typeparam name = "T">属性类型</typeparam>
        /// <param name = "obj"></param>
        /// <param name = "propertyName">属性名</param>
        /// <returns></returns>
        public static T GetPropertyValue<T>(this object obj, string propertyName)
        {
            return GetPropertyValue(obj, propertyName, default(T));
        }

        /// <summary>
        /// 通过反射动态取得对象的属性值
        /// </summary>
        /// <typeparam name = "T">属性类型</typeparam>
        /// <param name = "obj"></param>
        /// <param name = "propertyName">属性名</param>
        /// <param name = "defaultValue">默认值</param>
        /// <returns></returns>
        public static T GetPropertyValue<T>(this object obj, string propertyName, T defaultValue)
        {
            var property = obj.GetType().GetProperty(propertyName);

            if (property == null)
                throw new ArgumentException(string.Format("类型 '{1}' 未声明名为 '{0}' 的属性。", propertyName, obj), propertyName);

            var value = property.GetValue(obj, null);
            return (value is T ? (T) value : defaultValue);
        }

        /// <summary>
        /// 通过反射动态设置对象的属性值
        /// </summary>
        /// <param name = "obj"></param>
        /// <param name = "propertyName">属性名</param>
        /// <param name = "value">设定值</param>
        public static void SetPropertyValue(this object obj, string propertyName, object value)
        {
            var type = obj.GetType();
            var property = type.GetProperty(propertyName);

            if (property == null)
                throw new ArgumentException(string.Format("类型 '{1}' 未声明名为 '{0}' 的属性。", propertyName, type), propertyName);
            if (!property.CanWrite)
                throw new ArgumentException(string.Format("类型 '{1}' 的属性 '{0}' 为只读属性，不能进行赋值操作。", propertyName, type),
                                            propertyName);
            property.SetValue(obj, value, null);
        }

        /// <summary>
        /// 取得对象上定义的第一个匹配属性
        /// </summary>
        /// <typeparam name = "T">属性类型</typeparam>
        /// <param name = "obj"></param>
        /// <returns></returns>
        public static T GetAttribute<T>(this object obj) where T : Attribute
        {
            return GetAttribute<T>(obj, true);
        }

        /// <summary>
        /// 取得对象上定义的第一个匹配属性
        /// </summary>
        /// <typeparam name = "T">属性类型</typeparam>
        /// <param name = "obj"></param>
        /// <param name = "includeInherited">如果为<c>true</c>，表示包含继承属性</param>
        /// <returns></returns>
        public static T GetAttribute<T>(this object obj, bool includeInherited) where T : Attribute
        {
            var type = (obj as Type ?? obj.GetType());
            var attributes = type.GetCustomAttributes(typeof (T), includeInherited);
            return attributes.OfType<T>().FirstOrDefault();
        }

        /// <summary>
        /// 取得对象上定义的第一个匹配属性
        /// </summary>
        /// <typeparam name = "T">属性类型</typeparam>
        /// <param name = "member"></param>
        /// <returns></returns>
        public static T GetAttribute<T>(this ICustomAttributeProvider member) where T : Attribute
        {
            return member.GetCustomAttributes(typeof (T), false).OfType<T>().FirstOrDefault();
        }

        /// <summary>
        /// 取得对象上定义的所有匹配属性
        /// </summary>
        /// <typeparam name = "T">属性类型</typeparam>
        /// <param name = "obj"></param>
        /// <returns></returns>
        public static IEnumerable<T> GetAttributes<T>(this object obj) where T : Attribute
        {
            return GetAttributes<T>(obj, true);
        }

        /// <summary>
        /// 取得对象上定义的所有匹配属性
        /// </summary>
        /// <typeparam name = "T">属性类型</typeparam>
        /// <param name = "obj"></param>
        /// <param name = "includeInherited">如果为<c>true</c>，表示包含继承属性</param>
        /// <returns></returns>
        public static IEnumerable<T> GetAttributes<T>(this object obj, bool includeInherited) where T : Attribute
        {
            return
                obj.GetType().GetCustomAttributes(typeof (T), includeInherited).OfType<T>().Select(
                    attribute => attribute);
        }

        #endregion

        #region Other

        #region GetSafeValue By Reflction

        /*public static TResult GetSafeValue<TSource, TResult>(this TSource source,
                                                                 Expression<Func<TSource, TResult>> expression,
                                                                 TResult defaultValue)
        {
            var value = GetValue(expression, source);
            return value == null ? defaultValue : (TResult)value;
        }

        private static string GetFullPropertyPathName<TSource, TResult>(Expression<Func<TSource, TResult>> expression)
        {
            return expression.Body.ToString().Replace(expression.Parameters[0] + ".", string.Empty);
        }

        private static object GetValue<TSource, TResult>(Expression<Func<TSource, TResult>> expression, TSource source)
        {
            string fullPropertyPathName = GetFullPropertyPathName(expression);
            return GetNestedPropertyValue(fullPropertyPathName, source);
        }

        private static object GetNestedPropertyValue(string name, object obj)
        {
            PropertyInfo info;
            foreach (var part in name.Split('.'))
            {
                if (obj == null)
                {
                    return null;
                }
                if (obj is IEnumerable)
                {
                    var type = (obj as IEnumerable).GetType();
                    var methodInfo = type.GetMethod("get_Item");
                    var index = int.Parse(part.Split('(')[1].Replace(")", string.Empty));
                    try
                    {
                        obj = methodInfo.Invoke(obj, new object[] { index });
                    }
                    catch
                    {
                        obj = null;
                    }
                }
                else
                {
                    var type = obj.GetType();
                    info = type.GetProperty(part);
                    if (info == null)
                    {
                        return null;
                    }
                    obj = info.GetValue(obj, null);
                }
            }
            return obj;
        }*/

        #endregion

        #region GetSafeValue By Catch Exception

        /// <summary>
        /// GetSafeValue
        /// </summary>
        /// <typeparam name="TObject">object type.</typeparam>
        /// <typeparam name="TResult">return value type.</typeparam>
        /// <param name="obj"></param>
        /// <param name="getValue">the lamda expression.</param>
        /// <param name="defaultValue"></param>
        /// <example>
        /// <code>
        ///     var name = MyObject.GetSafeValue(p =&gt; p.MyProperty.MyListProperties[0].Name);
        /// </code>
        /// </example>
        /// <returns></returns>
        public static TResult GetSafeValue<TObject, TResult>(this TObject obj,
                                                             Func<TObject, TResult> getValue,
                                                             TResult defaultValue)
        {
            TResult result;
            try
            {
                result = getValue(obj);
            }
            catch
            {
                result = defaultValue;
            }
            //catch (NullReferenceException) { result = defaultValue; }
            //catch (IndexOutOfRangeException) { result = defaultValue; }
            //catch (ArgumentNullException) { result = defaultValue; }
            //catch (ArgumentOutOfRangeException) { result = defaultValue; }
            return result;
        }

        #endregion


        public static IDictionary<string, object> ToDictionary(this object instance)
        {
            Dictionary<string, object> dictionary = new Dictionary<string, object>();
            if (instance != null)
            {
                foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(instance))
                {
                    dictionary.Add(descriptor.Name, descriptor.GetValue(instance));
                }
            }
            return dictionary;
        }
        
        #endregion
    }
}